package DB.Usuario;

import DB.Util;
import DB.DBManager;
import DB.EntityBase;
import Model.Usuario;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;

/**
 *
 * @author pgrabin
 */
public class ControllerUsuario extends EntityBase<Usuario> {

    DBManager db;
    Connection conn;
    static ControllerUsuario uControl;

    public static ControllerUsuario getInstance() {
        if (uControl == null) {
            uControl = new ControllerUsuario();
        }

        return uControl;
    }

    public ControllerUsuario() {
        db = DBManager.getInstance();
        conn = db.getConn();
    }

    /**
     * Insere um novo USUARIO na base de dados.
     *
     * @param u o USUARIO a ser inserido.
     * @return o ID que o USUARIO recebeu, ou -1 se ocorreu algum erro.
     * @throws java.sql.SQLException
     */
    public long insert(Usuario u) throws SQLException {
        ResultSet generatedKeys = null;
        long idRetornado = -1;

        PreparedStatement ps = conn.prepareStatement("INSERT INTO usuarios VALUES (null, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS);
        ps.setString(1, u.getNome());
        ps.setString(2, u.getUsername());
        ps.setString(3, Util.encryptPassword(u.getPassword()));
        ps.setInt(4, u.getOperador());
        ps.setString(5, u.getCpf());
        ps.setString(6, u.getEmail());
        ps.setString(7, u.getDataNascimento());
        ps.setString(8, u.getNomeCracha());
        ps.setString(9, u.getSexo());
        ps.setString(10, u.getEndereco());
        ps.setString(11, u.getCidade());
        ps.setString(12, u.getEstado());
        ps.setString(13, u.getPais());
        ps.setString(14, u.getCep());
        ps.setString(15, u.getTelefone());
        ps.setString(16, u.getCelular());

        int affectedRows = ps.executeUpdate();
        if (affectedRows == 0) {
            throw new SQLException("Creating user failed, no rows affected.");
        }
        generatedKeys = ps.getGeneratedKeys();
        if (generatedKeys.next()) {
            idRetornado = generatedKeys.getLong(1);
        } else {
            throw new SQLException("Creating user failed, no generated key obtained.");
        }

        return idRetornado;
    }

    /**
     * Atualiza o registro na base de dados do usuario recebido como parametro,
     * tomando como base o ID.
     *
     * @param u O USUARIO que deve ter suas informações atualizadas.
     * @return Quantidade de linhas afetadas.
     * @throws java.sql.SQLException
     */
    @Override
    public long update(Usuario u) throws SQLException {
        ResultSet generatedKeys = null;
        long affectedRows = 0;

        PreparedStatement ps = conn.prepareStatement("UPDATE " + UsuariosTable.TABLE_NAME
                + " SET Nome = ?,"
                + " Username = ?,"
                + " Password = ?,"
                + " Operador = ?,"
                + " Cpf = ?,"
                + " Email = ?,"
                + " dataNascimento = ?,"
                + " NomeCracha = ?,"
                + " Sexo = ?,"
                + " Endereco = ?,"
                + " Cidade = ?,"
                + " Estado = ?,"
                + " Pais = ?,"
                + " Cep = ?,"
                + " Telefone = ?,"
                + " Celular = ?"
                + " WHERE id = ?", Statement.RETURN_GENERATED_KEYS);

        ps.setString(1, u.getNome());
        ps.setString(2, u.getUsername());
        ps.setString(3, u.getPassword());
        ps.setInt(4, u.getOperador());
        ps.setString(5, u.getCpf());
        ps.setString(6, u.getEmail());
        ps.setString(7, u.getDataNascimento());
        ps.setString(8, u.getNomeCracha());
        ps.setString(9, u.getSexo());
        ps.setString(10, u.getEndereco());
        ps.setString(11, u.getCidade());
        ps.setString(12, u.getEstado());
        ps.setString(13, u.getPais());
        ps.setString(14, u.getCep());
        ps.setString(15, u.getTelefone());
        ps.setString(16, u.getCelular());
        ps.setLong(17, u.getId());

        System.out.println(u.getId() + " - " + u.getNome());

        affectedRows = ps.executeUpdate();
        if (affectedRows == 0) {
            throw new SQLException("Update user failed, no rows affected.");
        } else if (affectedRows > 1) {
            throw new SQLException("More than one row updated");
        }

        return affectedRows;
    }

    /**
     * Retorna o USUARIO cujo ID é igual ao ID informado, Ou null caso não
     * encontre algum USUARIO com aquele ID.
     *
     * @param id ID do USUARIO que será retornado.
     * @return Retorna o USUARIO com o ID informado
     * @throws java.sql.SQLException
     */
    @Override
    public Usuario getByID(long id) throws SQLException {
        ResultSet rs;
        Usuario u = null;

        PreparedStatement ps = conn.prepareStatement("SELECT * FROM " + UsuariosTable.TABLE_NAME + " WHERE ID = ?");
        ps.setLong(1, id);

        rs = ps.executeQuery();

        if (rs.next()) {
            u = getFromResultSet(rs);
        } else {
            throw new SQLException("Usuario não encontrada.");
        }

        return u;
    }

    /**
     * Deleta da base de dados o registro com o ID informado
     *
     * @param id O ID do USUARIO que deve ser deletado.
     * @return Quantidade de linhas afetadas.
     * @throws java.sql.SQLException
     */
    @Override
    public int delete(long id) throws SQLException {
        int resultadoDelete = 0;

        PreparedStatement ps = conn.prepareStatement("DELETE FROM " + UsuariosTable.TABLE_NAME + " WHERE " + UsuariosTable.COLUMN_ID + " = ?");
        ps.setLong(1, id);

        resultadoDelete = ps.executeUpdate();

        if (resultadoDelete == 0) {
            throw new SQLException("No record found for deletion!");
        } else if (resultadoDelete > 1) {
            throw new SQLException("More than one row deleted!");
        } else {
            System.out.println("Usuario código " + id + " deletado!");
        }

        return resultadoDelete;
    }

    /**
     * Retorna todos os registros da tabela.
     *
     * @return Um arrayList<Usuario> contendo todos os registros.
     */
    @Override
    public ArrayList<Usuario> getAll() {
        ArrayList<Usuario> usuarios = new ArrayList<>();
        ResultSet rs = null;

        try {
            PreparedStatement ps = conn.prepareStatement("SELECT * FROM " + UsuariosTable.TABLE_NAME);
            rs = ps.executeQuery();

            while (rs.next()) {
                Usuario resultUser = getFromResultSet(rs);
                usuarios.add(resultUser);
            }
        } catch (SQLException e) {
            e.printStackTrace(System.err);
        }

        return usuarios;
    }

    /**
     * Retorna todos os registros da tabela que contem o campo operador = 0
     *
     * @return Um arrayList<Usuario> contendo todos os registros.
     * @throws java.sql.SQLException
     */
    public ArrayList<Usuario> getAllParticipantes() throws SQLException {
        ArrayList<Usuario> usuarios = new ArrayList<>();
        ResultSet rs;

        PreparedStatement ps = conn.prepareStatement("SELECT * FROM " + UsuariosTable.TABLE_NAME + " WHERE " + UsuariosTable.COLUMN_OPERADOR + " = " + 0);
        rs = ps.executeQuery();

        while (rs.next()) {
            Usuario resultUser = getFromResultSet(rs);
            usuarios.add(resultUser);
        }

        if (usuarios.isEmpty()) {
            throw new SQLException("Nenhum usuário cadastrado.");
        } else {
            return usuarios;
        }
    }

    /**
     * Retorna todos os registros da tabela que contem o campo operador = 1
     *
     * @return Um arrayList<Usuario> contendo todos os registros.
     * @throws java.sql.SQLException
     */
    public ArrayList<Usuario> getAllOperadores() throws SQLException {
        ArrayList<Usuario> usuarios = new ArrayList<>();
        ResultSet rs;

        PreparedStatement ps = conn.prepareStatement("SELECT * FROM " + UsuariosTable.TABLE_NAME + " WHERE " + UsuariosTable.COLUMN_OPERADOR + " = " + 1);
        rs = ps.executeQuery();

        while (rs.next()) {
            Usuario resultUser = getFromResultSet(rs);
            usuarios.add(resultUser);
        }

        if (usuarios.isEmpty()) {
            throw new SQLException("Nenhum usuário cadastrado.");
        } else {
            return usuarios;
        }
    }

    /**
     * Busca na tabela por um USUARIO que possua o username e o password
     * informados.
     *
     * @param username O username do usuario.
     * @param password O password do usuario.
     * @return O USUARIO, caso seja encontrado. Null, caso não seja e
     * @throws java.sql.SQLException
     */
    public Usuario login(String username, String password) throws SQLException {
        Usuario u = null;
        ResultSet rs;

        PreparedStatement ps;
        ps = conn.prepareStatement("SELECT * FROM " + UsuariosTable.TABLE_NAME
                + " WHERE " + UsuariosTable.COLUMN_USERNAME + " = ? "
                + " AND " + UsuariosTable.COLUMN_PASSWORD + " = ?");

        ps.setString(1, username);
        ps.setString(2, Util.encryptPassword(password));

        rs = ps.executeQuery();

        if (rs.next()) {
            Usuario aux = getFromResultSet(rs);
            u = aux;
        } else {
            throw new SQLException("Usuário/Senha inválidos!");
        }

        return u;
    }

    /**
     * Converte o resultSet do banco em um Usuario
     * @param rs o resultSet
     * @return o Usuario retornado
    */
    @Override
    public Usuario getFromResultSet(ResultSet rs) {
        Usuario u = null;

        try {
            int id = rs.getInt(1);
            String nome = rs.getString(UsuariosTable.COLUMN_NOME);
            String username = rs.getString(UsuariosTable.COLUMN_USERNAME);
            String password = rs.getString(UsuariosTable.COLUMN_PASSWORD);
            int operador = rs.getInt(UsuariosTable.COLUMN_OPERADOR);
            String cpf = rs.getString(UsuariosTable.COLUMN_CPF);
            String email = rs.getString(UsuariosTable.COLUMN_EMAIL);
            String dataNascimento = rs.getString(UsuariosTable.COLUMN_DATANASCIMENTO);
            String nomeCracha = rs.getString(UsuariosTable.COLUMN_NOMECRACHA);
            String sexo = rs.getString(UsuariosTable.COLUMN_SEXO);
            String endereco = rs.getString(UsuariosTable.COLUMN_ENDERECO);
            String cidade = rs.getString(UsuariosTable.COLUMN_CIDADE);
            String estado = rs.getString(UsuariosTable.COLUMN_ESTADO);
            String pais = rs.getString(UsuariosTable.COLUMN_PAIS);
            String cep = rs.getString(UsuariosTable.COLUMN_CEP);
            String telefone = rs.getString(UsuariosTable.COLUMN_TELEFONE);
            String celular = rs.getString(UsuariosTable.COLUMN_CELULAR);

            u = new Usuario(id, nome, username, password, operador, cpf, email, dataNascimento, nomeCracha, sexo, endereco, cidade, estado, pais, cep, telefone, celular);
//            System.out.println("usuarioGetFromResultSet retornando " + u.toString());
        } catch (SQLException e) {
            e.printStackTrace(System.err);
        }

        return u;
    }

    /**
     * Adiciona alguns registros padrão na tabela, para testes e demonstrações de usabilidade.
     * @throws java.sql.SQLException
    */
    public void standardUsuarios() throws SQLException {
        Usuario p, a, b, c, d, e;
        p = new Usuario("Paulo Henrique Grolli Grabin", "pgrabin", "pgrabin", 1, "027.432.730-97", "plgrabin@gmail.com", "30/08/1990", "Spiga", "Masculino", "Rua Sete", "Cidade grande", "Rio Grande", "aeHOOOOOOO", "99999999", "35904041", "99576664");
        a = new Usuario("Augusto Toshin Arikawa", "augusto", "augusto", 1, "511.573.847-30", "augusto.arikawa@sap.com", null, null, "Masculino", "Rua Sete", "Cidade grande", "Rio Grande", "aeHOOOOOOO", "99999999", "08006546545", "08006546545");
        b = new Usuario("Diego Figliero", "diego", "diego", 1, "233.945.439-58", "diegofigliero@gmail.com", null, null, "Masculino", "Rua Sete", "Cidade grande", "Rio Grande", "aeHOOOOOOO", "99999999", "08006546545", "08006546545");
        c = new Usuario("Juliano Bellini", "juliano", "juliano", 1, "144.183.036-77", "jubellini@gmail.com", null, null, "Masculino", "Rua Sete", "Cidade grande", "Rio Grande", "aeHOOOOOOO", "99999999", "08006546545", "08006546545");
        d = new Usuario("Rodrigo Melchiades", "rodrigo", "rodrigo", 1, "547.445.775-80", "rsmelchiades@gmail.com", null, null, "Masculino", "Rua Sete", "Cidade grande", "Rio Grande", "aeHOOOOOOO", "99999999", "08006546545", "08006546545");
        e = new Usuario("Usuario da Silva Regular User", "user", "user", 0, "848.859.688-01", "user@userdomain.com.us", null, null, "Masculino", "Rua Sete", "Cidade grande", "Rio Grande", "aeHOOOOOOO", "99999999", "08006546545", "08006546545");

        long pp = insert(p);
        long aa = insert(a);
        long bb = insert(b);
        long cc = insert(c);
        long dd = insert(d);
        long ee = insert(e);

        System.out.println("Usuarios padrão inseridos, ids: " + pp + " " + aa + " " + bb + " " + cc + " " + dd + " " + ee);
    }

    /**
     * Retorna a quantidade de registros na tabela.
     * 
     * @return a quantidade de registros;
     */
    @Override
    public int getRowsCount() {
        return db.getTableCount(UsuariosTable.TABLE_NAME);
    }

    public static void main(String args[]) {
//        ControllerUsuario cu = ControllerUsuario.getInstance();
//        Usuario u = new Usuario("Paulo", "pgrabin2", "pgrabin", 1, "02743273097", "plgrabin@gmail.com", "30/08/1990", "Spiga", "Masculino", "Endereço", "Cidade", "Estado", "Pais", "CEP", "35904041", "99576664");

//        long id = cu.insert(u);
//        System.out.println(id);
//        u.setPassword("aa");
//        System.out.println(u.getPassword().hashCode());;
//        Provider[] p = new Provider[50];
//        p = Security.getProviders();
//
//        for (int i = 0; i < p.length; i++) {
//            System.out.println(p[i].getInfo());
//        }
//        
        /**
         * INSERT
         */
//        long returnedID = cu.insert(u);
//        System.out.println("Usuario inserido recebeu o id: " + returnedID);
//        u = cu.getByID((int) returnedID);
//        System.out.println(u);
        /**
         * STANDARD USUARIOS
         */
//        cu.standardUsuarios();
        /**
         * UPDATE
         */
//        Usuario u = cu.getByID(21);
//        System.out.println("Nome velho: " + u.getNome());
//        u.setNome("Pauloooooooooooooooooooo");
//        
//        long linhasAfetadas = cu.update(u);
//        
//        u = null;
//        u = cu.getByID(21);
//        System.out.println("Nome novo: " + u.getNome());
        /**
         * DELETE
         */
//        cu.delete((int) returnedID);
//        u = cu.getByID((int) returnedID);
//        System.out.println(u);
        /**
         * GETALL
         */
//        System.out.println(cu.getAll());
        /**
         * LOGIN
         */
//        Usuario u = cu.login("pgrabiN", "pgrabin");
//        System.out.println(u);
//        System.out.println("Iniciando testes dos metodos de usuario...");
//        ControllerUsuario uc = ControllerUsuario.getInstance();
//
//        System.out.println();
//        System.out.println();
//        System.out.println("\t\tTestando INSERT:");
//        Usuario u = new Usuario("Paulo", "pgrabin", "pgrabin", 1, "02743273097", "plgrabin@gmail.com", "30/08/1990", "Spiga", "Masculino", "Endereço", "Cidade", "Estado", "Pais", "CEP", "35904041", "99576664");
//        long usuarioID = uc.insert(u);
//
//        System.out.println("Usuario recebeu o ID: " + usuarioID);
//        
//        u = null;
//        
//        System.out.println("Testando GET: ");
//        u = uc.getByID((int) usuarioID);
//        
//        System.out.println("Resultado do GET: " + u.toString());
//
//        System.out.println("Testando update...");
//        u.setNome("NOVO NOME AEAEAEAEAEA");
//        long resultUpdate = uc.update(u);
//        
//        
//        System.out.println("Testando getAll...");
//        System.out.println(uc.getAll());
//        
//        System.out.println("Testando delete...");
//        int resultDelete = uc.delete((int) usuarioID);
//        
//        System.out.println("Testando GET: ");
//        u = null;
//        u = uc.getByID((int) usuarioID);
//        if(u == null) {
//            System.out.println("Funcionou");
//        }
//        else System.out.println("Não funcionou");
    }
}
